//: C25:AbstractFactory.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 1999
// Copyright notice in Copyright.txt
// A gaming environment
#include <iostream>
using namespace std;

class Obstacle
{
public:
    virtual void action() = 0;
};

class Player
{
public:
    virtual void interactWith(Obstacle*) = 0;
};

class Kitty: public Player
{
    virtual void interactWith(Obstacle* ob) {
        cout << "Kitty has encountered a ";
        ob->action();
    }
};

class KungFuGuy: public Player
{
    virtual void interactWith(Obstacle* ob) {
        cout << "KungFuGuy now battles against a ";
        ob->action();
    }
};

class Puzzle: public Obstacle
{
public:
    void action() {
        cout << "Puzzle\n";
    }
};

class NastyWeapon: public Obstacle
{
public:
    void action() {
        cout << "NastyWeapon\n";
    }
};

// The abstract factory:
class GameElementFactory
{
public:
    virtual Player* makePlayer() = 0;
    virtual Obstacle* makeObstacle() = 0;
};

// Concrete factories:
class KittiesAndPuzzles :
        public GameElementFactory
{
public:
    virtual Player* makePlayer() {
        return new Kitty;
    }
    virtual Obstacle* makeObstacle() {
        return new Puzzle;
    }
};

class KillAndDismember :
        public GameElementFactory
{
public:
    virtual Player* makePlayer() {
        return new KungFuGuy;
    }
    virtual Obstacle* makeObstacle() {
        return new NastyWeapon;
    }
};

class GameEnvironment
{
    GameElementFactory* gef;
    Player* p;
    Obstacle* ob;
public:
    GameEnvironment(GameElementFactory* factory) :
            gef(factory), p(factory->makePlayer()),
            ob(factory->makeObstacle()) {}
    void play() {
        p->interactWith(ob);
    }
    ~GameEnvironment() {
        delete p;
        delete ob;
        delete gef;
    }
};

int main()
{
    GameEnvironment
    g1(new KittiesAndPuzzles),
    g2(new KillAndDismember);
    g1.play();
    g2.play();
} ///:~
